#include "TextureUnitGL3x.h"
#include "TextureSampler.h"

using namespace Renderer;
using namespace Renderer::GL3x;


void Renderer::GL3x::TextureUnitGL3x::Validate()
{
	if (_texture)
	{
		if (!_textureSampler)
		{
			//A texture sampler must be assigned to a texture unit with one or more bound textures.
			assert(false);
		}
		if (_texture->GetTarget() == GL_TEXTURE_RECTANGLE)
		{
			if (_textureSampler->GetMinificationFilter() != TextureMinificationFilter::Linear &&
				_textureSampler->GetMinificationFilter() != TextureMinificationFilter::Nearest)
			{
				//The texture sampler is incompatible with the rectangle texture bound to the same texture unit.  Rectangle textures only support linear and nearest minification filters.
				assert(false);
			}
			if (_textureSampler->GetWrapS() == TextureWrap::Repeat ||
				_textureSampler->GetWrapS() == TextureWrap::MirroredRepeat ||
				_textureSampler->GetWrapT() == TextureWrap::Repeat ||
				_textureSampler->GetWrapT() == TextureWrap::MirroredRepeat)
			{
				//The texture sampler is incompatible with the rectangle texture bound to the same texture unit.  Rectangle textures do not support repeat or mirrored repeat wrap modes.
				assert(false);
			}
		}

	}
}

void Renderer::GL3x::TextureUnitGL3x::Clean()
{
	if (_dirtyFlags != DirtyFlags::None)
	{
		Validate();

		glActiveTexture(_textureUnit);

		assert(glGetError() == GL_NO_ERROR);
		if ((_dirtyFlags & DirtyFlags::Texture) == DirtyFlags::Texture)
		{
			if (_texture)
			{
				_texture->Bind();
			}
			else
			{
				Texture2DGL3x::UnBind(GL_TEXTURE_2D);
				Texture2DGL3x::UnBind(GL_TEXTURE_RECTANGLE);
			}
		}

		if ((_dirtyFlags & DirtyFlags::TextureSampler) == DirtyFlags::TextureSampler)
		{
			if (_textureSampler)
			{
				_textureSampler->Bind(_textureUnitIndex);
			}
			else
			{
				TextureSamplerGL3x::UnBind(_textureUnitIndex);
			}
		}
		_dirtyFlags = DirtyFlags::None;
	}
}

void Renderer::GL3x::TextureUnitGL3x::CleanLastTextureUnit()
{
	//
	// If the last texture unit has a texture attached, it
	// is cleaned even if it isn't dirty because the last
	// texture unit is used for texture uploads and downloads in
	// Texture2DGL3x, the texture unit could be dirty without
	// knowing it.
	//
	if (_texture)
	{
		_dirtyFlags = (DirtyFlags)(_dirtyFlags | DirtyFlags::Texture);
	}
	Clean();
}

void Renderer::GL3x::TextureUnitGL3x::SetTextureSampler(Renderer::TextureSampler* ts)
{
	auto sampler = (TextureSamplerGL3x*)ts;
	if (_textureSampler != sampler)
	{
		if (_dirtyFlags == DirtyFlags::None)
		{
			_observer->NotifyDirty(this);
		}

		_dirtyFlags = (DirtyFlags)(_dirtyFlags | DirtyFlags::TextureSampler);
		_textureSampler = sampler;
	}
}

TextureSampler* Renderer::GL3x::TextureUnitGL3x::GetTextureSampler() const
{
	return _textureSampler;
}

void Renderer::GL3x::TextureUnitGL3x::SetTexture(Texture2D* t)
{
	auto texture = (Texture2DGL3x*)t;
	if (_texture != texture)
	{
		if (_dirtyFlags == DirtyFlags::None)
		{
			_observer->NotifyDirty(this);
		}

		_dirtyFlags = (DirtyFlags)(_dirtyFlags | DirtyFlags::Texture);
		_texture = texture;
	}
}

Texture2D* Renderer::GL3x::TextureUnitGL3x::GetTexture() const
{
	return _texture;
}

Renderer::GL3x::TextureUnitGL3x::TextureUnitGL3x(int index, ICleanableObserver* observer)
{
	_textureUnitIndex = index;
	_textureUnit = GL_TEXTURE0 + index;
	_observer = observer;
}
